const { reject } = require("lodash");

/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
    // promise 状态
    status = PENDING;
    // 成功的值
    value = undefined;
    // 失败原因
    reason = undefined;
    handleSuccess = [];
    handleFail = [];
    // new Promise((resolve, reject) => {})
    constructor(callback) {
        callback(this.resolve, this.reject);
    }
    static resolve(val) {
        // Promise.resolve，如果参数是 promise 则直接返回
        if (val instanceof MyPromise) {
            return val;
        }
        return new MyPromise((resolve) => {
            resolve(val);
        });
    }
    static all(arr) {
        const res = [];
        const len = arr.length;
        let index = 0;
        // 计数法，确保所有异步调用完了再 resolve
        const addItem = (k, val, resolve) => {
            res[k] = val;
            index++;
            if (index === len) {
                resolve(res);
            }
        };
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < len; i++) {
                const current = arr[i];
                // 如果是 promise 就用 then 查看状态
                if (current instanceof MyPromise) {
                    current.then((val) => addItem(i, val, resolve), reject);
                } else {
                    addItem(i, current, resolve);
                }
            }
            // 异步还没结束，这边已经 resolve 会导致结果出错，需要换个方法来 resolve
            // resolve(res)
        });
    }
    // 使用箭头函数确保 this 依旧指向 MyPromise 的实例
    resolve = (value) => {
        // promise 状态改变后就不能再改了
        if (this.status !== PENDING) return;
        this.value = value;
        this.status = FULFILLED;
        // 异步完成后需要调用对应回调
        while (this.handleSuccess.length) this.handleSuccess.shift()();
    };
    reject = (reason) => {
        if (this.status !== PENDING) return;
        this.reason = reason;
        this.status = REJECTED;
        while (this.handleFail.length) this.handleFail.shift()();
    };
    // promise.then(() => {}, () => {})
    then(success, fail) {
        success = isFunc(success) ? success : (value) => value;
        fail = isFunc(fail)
            ? fail
            : (reason) => {
                  throw reason;
              };
        // then 会返回新的 promise
        const promise2 = new MyPromise((resolve, reject) => {
            // 成功时
            if (this.status === FULFILLED) {
                // 返回的值可能会是 promise，所以需要判断
                // 为了得到返回的 promise 对象，使用 setTimeout 将任务放到下个事件循环中处理
                setTimeout(() => {
                    // success 可能会抛出异常，也需要捕获住
                    try {
                        const res = success(this.value);
                        resolvePromise(promise2, res, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                // 失败时
                setTimeout(() => {
                    try {
                        // catch 返回的非 promise 值也会 resolve 处理
                        const err = fail(this.reason);
                        resolvePromise(promise2, err, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else {
                // 异步尚未改变状态，保存回调
                // 同一个 promise 可以多次调用 then，所以使用数组保存
                // 上面做的判断限制这里也需要做，所以这里传递的函数就要修改下
                // this.handleSuccess.push(success);
                // this.handleFail.push(fail);
                this.handleSuccess.push(() => {
                    setTimeout(() => {
                        // success 可能会抛出异常，也需要捕获住
                        try {
                            const res = success(this.value);
                            resolvePromise(promise2, res, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                this.handleFail.push(() => {
                    setTimeout(() => {
                        try {
                            // catch 返回的非 promise 值也会 resolve 处理
                            const err = fail(this.reason);
                            resolvePromise(promise2, err, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });
        return promise2;
    }
    catch(fn) {
        return this.then(undefined, fn);
    }
    // catch , finally 类似，接在 then 后都需要判断下上一个 promise 的状态，判断状态就使用 then
    // finally 不接受任何之前的状态，所以不需要传递参数
    // finally 的返回值不会往后传递，但是如果 finally 返回的还是 promise 的话，也需要用 then 查看状态再往后传递
    // finally 返回一个 promise
    finally(fn) {
        return this.then(
            (val) => {
                const res = fn();
                if (res instanceof MyPromise) {
                    return res.then(
                        () => val,
                        (err) => {
                            // then 中逻辑处理都用 try..catch 包裹了，所以这里直接抛出就行了
                            throw err;
                        }
                    );
                }
                return MyPromise.resolve(val);
            },
            (reason) => {
                const res = fn();
                if (res instanceof MyPromise) {
                    return res.then(
                        () => reason,
                        (err) => {
                            // then 中逻辑处理都用 try..catch 包裹了，所以这里直接抛出就行了
                            throw err;
                        }
                    );
                }
                return MyPromise.resolve(reason);
            }
        );
    }
}

const resolvePromise = (promise, val, resolve, reject) => {
    if (val instanceof MyPromise) {
        // 如果 then 返回的 promise 与 success 返回的 promise 是同一个，
        // 就报死循环错误
        if (promise === val) {
            return reject(new TypeError("死循环了啊啊啊啊"));
        }
        // 如果是 promise 则需要查看状态，并且在状态改变时需要将值存到返回的 promise 对象上
        return val.then(resolve, reject);
    }
    resolve(val);
};

const isFunc = (val) =>
    Object.prototype.toString.call(val).slice(8, 16) === "Function";

const p1 = new MyPromise((resolve, reject) => {
    resolve("abc");
});

const p2 = new MyPromise((resolve, reject) => {
    reject("zzz");
});

// p1.then((val) => {
//     console.log(val);
//     return "def";
// })
//     .then((val) => {
//         console.log(val);
//         return p2;
//     })
//     .catch((err) => {
//         console.log(err);
//         return 100;
//     })
//     .then((val) => {
//         console.log(val);
//     });

MyPromise.resolve(123).then((val) => {
    console.log(val);
});

// MyPromise.resolve(
//     new MyPromise((re, rj) => {
//         re(123);
//     })
// )
//     .then(() => {
//         return 456;
//     })
//     .finally((val) => {
//         console.log("123", val);
//         return 789;
//     })
//     .then((val) => {
//         console.log(val);
//     });
